#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <pngwriter.h>
#include <stdlib.h>
#include <complex>
#include <math.h>
#include <stdio.h>
#include <stdarg.h>
#include <err.h>
#include "rotation.h"

#define XF_COMPONENT_ANGLES 1
#define XF_MATRICES 2
#define XF_METHOD XF_MATRICES

//using namespace std;

int width = 1920;
int height = 1080;

int r_min = 0;
int g_min = 0;
int b_min = 0;
int r_max = 0;
int g_max = 0;
int b_max = 0;

float rot_wx = 0;
float rot_xy = 0;
float rot_yz = 0;
float rot_zw = 0;
float rot_wy = 0;
float rot_xz = 0;

int batch_mode = 0;

char *dataset="out.txt";
const char *default_filename = "out.png";
char *filename= NULL;

float *r_img;
float *b_img;
float *g_img;

int index(int,int);
void clear_array(float *);
void inc_point(float,float,float,float,int);
void trace(float, float, float, float, int);
void make_image(char*);
float getMax(float*);
void print_help();
int readin_consts();

void print_message(char* fmt, ...) {
	va_list ap;
	char* r_fmt;
	asprintf(&r_fmt, batch_mode ? "M %s\n" : "%s\x1b[K\n", fmt);
	va_start(ap, fmt);
	vprintf(r_fmt, ap);
	va_end(ap);
}

void print_progress(int progress) {
	if (batch_mode) {
		printf("P %d\n", progress);
	} else {
		printf("%d%% Complete\x1b[K\r", progress);
	}
}

int main(int argc, char **argv){

  //Option parsing.
  char c = 0;
  filename = strdup(default_filename);
  while ((c = getopt (argc, argv, "bi:o:h:w:")) != -1){
       switch(c){
       case 'b':
	       batch_mode = 1;
	       break;
            case 'i'://input file
                dataset = optarg;
                break;
            case 'o'://output file
		    free(filename);
		    filename = strdup(optarg);
                break;
            case 'h':
                height = atoi(optarg);
                break;
            case 'w':
                width = atoi(optarg);
                break;
            case '?'://unknown opt, print help then quit.
                print_help();
                abort();
       }
  }


   //alloc our buffers.
   r_img = (float *)malloc(width*height*sizeof(float));
   b_img = (float *)malloc(width*height*sizeof(float));
   g_img = (float *)malloc(width*height*sizeof(float));

   FILE* f_dataset = fopen(dataset, "r");
   do {
	   if (!readin_consts())
		   return 0;

	   clear_array(r_img);
	   clear_array(b_img);
	   clear_array(g_img);



	   fseek(f_dataset, 0, SEEK_SET);
	   while(!feof(f_dataset)){
		   float a;
		   float b;
		   float cr;
		   float ci;
		   int count;
		   fscanf(f_dataset, "%f %f %f %f %d\n", &a, &b, &cr, &ci, &count);

		   trace(a,b,cr,ci,count);
	   }

	   make_image(filename);
	   if (batch_mode)
		   printf("C\n");
   } while (batch_mode); // repeat if not in external mode...

   fclose(f_dataset);

   free(r_img);
   free(b_img);
   free(g_img);

   return 0;
}


void trace(float a, float b, float cr, float ci, int final_ct){


	std::complex<float> z (a,b);
	std::complex<float> c (cr, ci);

  int count = 0;

  //cout<<a<<" "<<b<<"="<<a*a+b*b<<"\n";

  while(count<=final_ct && a*a+b*b< 2){
     z = pow(z,2)+c;

     a = z.real();
     b = z.imag();

     inc_point(a,b,cr,ci,final_ct);

     count++;

  }

}

void make_image(char*filename){

   pngwriter png(width,height,0,filename);

   float r_max = getMax(r_img);
   float g_max = getMax(g_img);
   float b_max = getMax(b_img);

   for(int x = 0; x< width; x++){
      for(int y = 0; y< height; y++){
         float r = r_img[index(x,y)]/r_max;
         float g = g_img[index(x,y)]/g_max;
         float b = b_img[index(x,y)]/b_max;

         png.plot(x,y,r,g,b);
      }
   }

   png.close();

}


void inc_point(float a, float b, float cr, float ci,int count){
	float points[4]={a,b,cr,ci};
     transformXYZW(points);

     int x = (points[0])*(width/6.0)+(width/2.0);
     int y = (points[1])*(width/6.0)+(height/2.0);

     if(x>0&&y>0&&x<width&&y<height){
        int i = index(x,y);
        if(count<b_max && count>b_min){
           b_img[i]++;
        }
        if(count<g_max && count>g_min){
            g_img[i]++;
        }
        if(count<r_max && count>r_min){
            r_img[i]++;
        }
     }
}

void clear_array(float * arr){
   for(int x = 0; x<width*height; x++){
      arr[x]=0;
   }
}

int index(int x, int y){
   return (int)(x)+(int)(y*width);
}

float getMax(float * buf){
  float max = 0;
  for(int x = 0; x<width*height; x++){
     if(max<buf[x]){
        max = buf[x];
     }
  }
  return max;
}


int readin_consts(){
	if (!batch_mode) {
		printf("blue min and max?\n");
		scanf("%d %d", &b_min, &b_max);

		printf("green min and max?\n");
		scanf("%d %d", &g_min, &g_max);

		printf("red min and max?\n");
		scanf("%d %d", &r_min, &r_max);

		printf("rotations (wx, xy, yz, zw, wy, and xz)?\n");
		scanf("%f %f %f %f %f %f", &rot_wx, &rot_xy, &rot_yz, &rot_zw, &rot_wy, &rot_xz);
		printf("working...\n");

		matrix_ident(p_transform);
		mat_rot_WY(rot_wy);
		mat_rot_XZ(rot_xz);
		mat_rot_XY(rot_xy);
		mat_rot_YZ(rot_yz);
		mat_rot_ZW(rot_zw);
		mat_rot_WX(rot_wx);

		return 1;
	} else {
		/*
		 * Batch mode
		 * ----------
		 * Reads a sequence of lines of the form:
		 *
		 * <rmin> <rmax> <gmin> <gmax> <bmin> <bmax> <r_00> <r_01> .. <r_03> <r_10> .. <r_33> <output_file>
		 *
		 * where [rgb]{min,max} are the min and max for red,
		 * green and blue, respectively, and r_[0-3][0-3] are
		 * the coefficients of the 4-d rotation matrix.
		 *
		 * For each line, it computes the image, possibly
		 * providing status updates of the form: "M <message>"
		 * (representing a free-form message), or "P <percentage>"
		 * (representing a completion percentage). On completion,
		 * it prints "C", and waits for a new command.
		 *
		 * It is designed for use by an external driver program.
		 */

		int i = scanf("%d %d %d %d %d %d", &r_min, &r_max, &g_min, &g_max, &b_min, &b_max);
		if (i < 6) {
			if (i > 0)
				print_message("Not enough ranges");
			return 0;
		}
		int j;
		for (i = 0; i < 4; i++)
			for (j = 0; j < 4; j++)
				if(scanf("%f ", &p_transform[i][j]) < 1) {
					print_message("Not enough matrix elements");
					return 0;
				}

		free(filename);
		filename = NULL;
		size_t sz = 0;
		int l = getline(&filename, &sz, stdin);
		if (l < 1) {
			printf("Missing filename");
			return 0;
		}
		filename[l-1] = 0;
		return 1;
	}
}

void print_help(){
	fprintf(stderr,
		"mk_image: \n"
		"Visualize a buddhabrot dataset onto a .png\n"
		"\nOptions:\n"
		"\t-e            Run in batch mode. See source coments for details.\n "
		"\t-i <file>     Use <file> as input dataset.\n"
		"\t-o <file>     Output to <file> PNG file.  \n"
		"\t-w WIDTH      Width of output in pixels\n"
		"\t-h HEIGHT     Height of output in pixels\n"
		"\n\nExample:\n\tmk_image -i out.txt -o pict.png\n"
		"\n\nSee also:\n\tmk_buddhabrot_file\n");
}


